package collaboration.ui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.io.IOException;

import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;

import ocsf.server.ConnectionToClient;

import collaboration.client.CollabClient;
import collaboration.common.CollabIF;
import collaboration.common.CollaborationApp;
import collaboration.common.apps.*;
import collaboration.server.CollabServer;

//TODO Write the Comments
/**
 * The <code>CollabClientUI</code> implements <code>CollabIF, ActionListener</code> and contains 
 * all the methods necessary to create an instance of <code>CollabClientUI</code>.
 * 
 * @author Trevor Clack 6286191
 * @author Bowen Cheng 6204467
 * @version November 26, 2012 (1.46)
 */
public class CollabClientUI implements CollabIF, ActionListener {

	//------------------------
	// MEMBER VARIABLES
	//------------------------

	//collabClientUI Associations
	private CollabClient thisCollabClient;

	private final String clientName = CollabClient.PROGRAM_NAME;
	private final String serverName = CollabServer.PROGRAM_NAME;
	private final String newLine = CollabClient.NEWLINE;
	private final int numTabs = CollabClient.maximumNumberOfCollaborationApps();
	private final String clientCommands[][] = CollabClient.COMMANDS;
	private final String serverCommands[][] = CollabServer.COMMANDS;
	private final int  numClientCommands = clientCommands.length;
	private final int  numServerCommands = serverCommands.length;
	private final String validApps[][] = CollabClient.validApps;
	private final String defaultHost = CollabClient.DEFAULT_HOST;
	private final int defaultPort = CollabClient.DEFAULT_PORT;

	//collabClientUI GUI
	//Main Window
	private JFrame mainWindow;
	//Support Windows
	private JPanel appPanel, messagePanel, chatPanel, serverPanel;
	private JTabbedPane tabbedPane;
	private JScrollPane chatScrollPane, chatEnterScrollPane, serverScrollPane;
	private CollaborationApp app1, app2, app3, app4, app5;
	private CollaborationApp tabbedApps[] = {app1, app2, app3, app4, app5};
	private JTextArea chatTextArea, chatEnterArea, serverArea;
	private JButton sendChatText;
	//Menu GUI
	private JMenuBar mainMenuBar;
	private JMenu collaborateMenu, hostMenu, joinMenu, openMenu, closeMenu,
	collaborateApps, runningApps;
	private JMenuItem host, join,
	tab1, tab2, tab3, tab4, tab5, notHosting, collaborateClose;
	private JMenuItem tabbedMenu[] = {tab1, tab2, tab3, tab4, tab5};

	//------------------------
	// CONSTRUCTOR
	//------------------------

	/**
	 * The constructor for <code>CollabClientUI</code>.
	 * 
	 * @param aCollabClient The instance of <code>CollabClient</code>.
	 */
	public CollabClientUI(CollabClient aCollabClient)
	{
		boolean didAddCollabClient = setCollabClient(aCollabClient);
		if (!didAddCollabClient)
		{
			throw new RuntimeException("Unable to create collabClientUI due to collabClient");
		}
		createAndShowGUI();
	}
	
	/**
	 * reates the GUI for this instance of <code>CollabClient</code>.
	 */
	private void createAndShowGUI() {
		//Create and set up the window.
		/*Create Window*/
		mainWindow = new JFrame(clientName);
		Dimension windowsize = new Dimension(800,480);
		mainWindow.setPreferredSize(windowsize);
		mainWindow.setMinimumSize(windowsize);
		mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		mainWindow.setLayout(new BorderLayout());

		Dimension panelSize = new Dimension((windowsize.width/3),(windowsize.height-175));

		//Create the menu bar.
		mainMenuBar = new JMenuBar();

		//Build the Collaborate menu.
		//Collaborate
		collaborateMenu = new JMenu("Collaborate");
		collaborateMenu.setMnemonic(KeyEvent.VK_L);
		collaborateMenu.getAccessibleContext().setAccessibleDescription(
				"Collaborate");
		mainMenuBar.add(collaborateMenu);

		//Collaborate -> host
		host = new JMenuItem("Host");
		host.setMnemonic(KeyEvent.VK_H);
		host.getAccessibleContext().setAccessibleDescription(
				"Host");
		host.addActionListener(this);
		host.setToolTipText("Host and join your own " + serverName);
		collaborateMenu.add(host);

		//Collaborate -> join
		join = new JMenuItem("Join");
		join.setMnemonic(KeyEvent.VK_J);
		join.getAccessibleContext().setAccessibleDescription(
				"Join a " + serverName);
		join.addActionListener(this);
		join.setToolTipText("Join a " + serverName);
		collaborateMenu.add(join);

		//Build the Server menu.
		//Server
		hostMenu = new JMenu("Server");
		hostMenu.setMnemonic(KeyEvent.VK_S);
		hostMenu.getAccessibleContext().setAccessibleDescription(
				"Server Menu");
		mainMenuBar.add(hostMenu);

		JMenuItem command;
		//Server -> Commands
		for(int comm = 0; comm < numServerCommands; comm++){
			command = new JMenuItem(serverCommands[comm][0]);
			command.getAccessibleContext().setAccessibleDescription(
					serverCommands[comm][1]);
			command.setToolTipText(serverCommands[comm][1]);
			command.addActionListener(this);
			hostMenu.add(command);
		}

		//Build the Client menu.
		//Client
		joinMenu = new JMenu("Client");
		joinMenu.setMnemonic(KeyEvent.VK_J);
		joinMenu.getAccessibleContext().setAccessibleDescription(
				"Client Menu");
		mainMenuBar.add(joinMenu);

		//Client -> Commands
		for(int comm = 0; comm < numClientCommands; comm++){
			command = new JMenuItem(clientCommands[comm][0]);
			command.getAccessibleContext().setAccessibleDescription(
					clientCommands[comm][1]);
			command.setToolTipText(clientCommands[comm][1]);
			command.addActionListener(this);
			joinMenu.add(command);
		}

		//Build the Open menu.
		//Open
		openMenu = new JMenu("Open");
		openMenu.setMnemonic(KeyEvent.VK_O);
		openMenu.getAccessibleContext().setAccessibleDescription(
				"Open Menu");
		mainMenuBar.add(openMenu);

		//Open -> Collaboration App
		collaborateApps = new JMenu("Collaboration App");
		collaborateApps.setMnemonic(KeyEvent.VK_A);
		collaborateApps.getAccessibleContext().setAccessibleDescription(
				"Collaboration App");
		openMenu.add(collaborateApps);

		//Collaboration App menuItem
		JMenuItem collapp;
		for(int app = 0; app < validApps.length; app++){
			collapp = new JMenuItem(validApps[app][0]);
			collapp.getAccessibleContext().setAccessibleDescription(
					"Start a new " + validApps[app][0] + " application.");
			collapp.setToolTipText("Start a new " + validApps[app][0] + " application.");
			collapp.addActionListener(this);
			collaborateApps.add(collapp);
		}

		//Build the Close menu.
		//Close
		closeMenu = new JMenu("Close");
		closeMenu.setMnemonic(KeyEvent.VK_C);
		closeMenu.getAccessibleContext().setAccessibleDescription(
				"Close");
		mainMenuBar.add(Box.createHorizontalGlue());//move menu to rightmost position
		mainMenuBar.add(closeMenu);

		//Close -> Running Applications
		runningApps = new JMenu("Running Applications");
		runningApps.setMnemonic(KeyEvent.VK_R);
		runningApps.getAccessibleContext().setAccessibleDescription(
				"Running Applications");
		closeMenu.add(runningApps);

		//Close -> Running Applications -> Not Hosting
		notHosting = new JMenuItem("Not Hosting");
		notHosting.setMnemonic(KeyEvent.VK_N);
		notHosting.getAccessibleContext().setAccessibleDescription(
				"You are not hosting a "
						+ serverName);
		notHosting.setToolTipText("You are not hosting a "
				+ serverName);
		runningApps.add(notHosting);

		//Close -> Collaboration Program
		collaborateClose = new JMenuItem(clientName);
		collaborateClose.setMnemonic(KeyEvent.VK_P);
		collaborateClose.getAccessibleContext().setAccessibleDescription(
				"Close and Exit " + clientName);
		collaborateClose.setToolTipText(
				"Close and Exit " + clientName);
		collaborateClose.addActionListener(this);
		closeMenu.addSeparator();
		closeMenu.add(collaborateClose);

		/*Configure Panel*/
		appPanel = new JPanel(false);
		appPanel.setLayout(new GridLayout(1,1));
		appPanel.setPreferredSize(panelSize);
		appPanel.setMinimumSize(panelSize);

		tabbedPane = new JTabbedPane();
		//Add the tabs to tab panel and menu
		int keyEvent = KeyEvent.VK_1;
		for (Integer tab = 0; tab < numTabs; tab++){
			String tabText = " " + ((Integer)(tab + 1)).toString();
			tabbedPane.addTab(tabText, tabbedApps[tab]);
			tabbedPane.setMnemonicAt(tab, (keyEvent + tab));
			tabbedPane.setToolTipTextAt(tab, tabText);
			tabbedMenu[tab] = new JMenuItem("Close ");
			tabbedMenu[tab].setAccelerator(KeyStroke.getKeyStroke((keyEvent + tab),ActionEvent.CTRL_MASK));
			tabbedMenu[tab].setToolTipText("Close ");
			tabbedMenu[tab].getAccessibleContext().setAccessibleDescription(tabText);
			tabbedMenu[tab].addActionListener(this);
			runningApps.add(tabbedMenu[tab]);
		}      
		//Add the tabbed pane to this panel.
		appPanel.add(tabbedPane);
		//The following line enables to use scrolling tabs.
		tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);

		/*Configure Message Panel*/
		messagePanel = new JPanel(false);
		messagePanel.setLayout(new GridLayout(1,1));
		messagePanel.setPreferredSize(panelSize);
		messagePanel.setMinimumSize(panelSize);

		chatTextArea = new JTextArea(1,1);
		chatTextArea.setEditable(false);
		chatTextArea.setLineWrap(true);
		chatTextArea.setWrapStyleWord(true);
		chatTextArea.setToolTipText("Chat text window. Newest chats on top.");
		chatScrollPane = new JScrollPane(chatTextArea);
		chatScrollPane.setHorizontalScrollBarPolicy(
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		chatScrollPane.setVerticalScrollBarPolicy(
				JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
		messagePanel.add(chatScrollPane);

		/*Configure Chat Panel*/
		Dimension chatPanelSize = new Dimension(200,30);
		chatPanel = new JPanel(false);
		chatPanel.setLayout(new BorderLayout());
		chatPanel.setPreferredSize(chatPanelSize);
		chatPanel.setMinimumSize(chatPanelSize);

		chatEnterArea = new JTextArea(1, 30);
		chatEnterArea.setToolTipText("Enter chat text here then press the send button!");
		chatEnterScrollPane = new JScrollPane(chatEnterArea);
		chatEnterScrollPane.setHorizontalScrollBarPolicy(
				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		chatEnterScrollPane.setVerticalScrollBarPolicy(
				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		chatPanel.add(chatEnterScrollPane, BorderLayout.CENTER);

		sendChatText = new JButton("Send");
		sendChatText.setToolTipText("Send the chat text.");
		sendChatText.addActionListener(this);
		chatPanel.add(sendChatText, BorderLayout.LINE_END);

		/*Configure Server Panel*/
		serverPanel = new JPanel(false);
		serverPanel.setLayout(new BorderLayout());
		serverPanel.setPreferredSize(panelSize);
		serverPanel.setMinimumSize(panelSize);

		serverArea = new JTextArea(1,1);
		serverArea.setEditable(false);
		serverArea.setLineWrap(true);
		serverArea.setWrapStyleWord(true);
		serverArea.setToolTipText("Server status messages.");
		serverScrollPane = new JScrollPane(serverArea);
		serverScrollPane.setHorizontalScrollBarPolicy(
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		serverScrollPane.setVerticalScrollBarPolicy(
				JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
		serverPanel.add(serverScrollPane, BorderLayout.CENTER);

		//Add content to the window.
		mainWindow.add(mainMenuBar, BorderLayout.PAGE_START);
		mainWindow.add(messagePanel, BorderLayout.LINE_START);
		mainWindow.add(appPanel, BorderLayout.CENTER);
		mainWindow.add(serverPanel, BorderLayout.LINE_END);
		mainWindow.add(chatPanel, BorderLayout.PAGE_END);

		//Display the window.
		mainWindow.pack();
		mainWindow.setVisible(true);
		resetWindow();
	}
	
	/**
	 * Resets the GUI to the default
	 */
	private void resetWindow(){
	//Remove components for default startup
			hostMenu.setVisible(false);
			joinMenu.setVisible(false);
			openMenu.setVisible(false);
			notHosting.setVisible(false);
			resetTabs();
			tabbedPane.setVisible(false);
			serverPanel.setVisible(false);
			collaborateMenu.setVisible(true);
	}
	
	/**
	 * Catches the window closing in order to properly release it's resources.
	 * 
	 * @param e The instance of <code>WindowEvent</code> when the window closes.
	 */
	public void windowClosing(WindowEvent e) {
		thisCollabClient.handleMessageFromClientUI("#quit");
		mainWindow.dispose();
		System.exit(0);
	}
	
	/**
	 * Sets up the GUI for hosting a <code>CollabServer</code>.
	 */
	private void hosting(){
		int portToHostAndConnect = setPort("host");
		if (!(portToHostAndConnect == -1)){
			thisCollabClient.setPort(portToHostAndConnect);//Set the port to host and connect on
			try {
				if(!(thisCollabClient.getCollabServer()==null) ||
						(thisCollabClient.setCollabServer(new CollabServer(thisCollabClient.getPort(),thisCollabClient)))){
					thisCollabClient.getCollabServer().listen();
					thisCollabClient.handleMessageFromClientUI("#login");
					serverPanel.setVisible(true);
					hostMenu.setVisible(true);
					joinMenu.setVisible(true);
					openMenu.setVisible(true);
					notHosting.setVisible(false);
					collaborateMenu.setVisible(false);
					resetTabs();
					tabbedPane.setVisible(true);
				}
			} catch (IOException e) {
				JOptionPane.showMessageDialog(
						mainWindow,
						"ERROR: Unable to start hosting on " + portToHostAndConnect + "!",
						"Unable to Host",
						JOptionPane.ERROR_MESSAGE);
			}
		}
	}

	/**
	 * Sets up the GUI for joining a <code>CollabServer</code>.
	 */
	private void joining(){
		String hostToConnectTo = setHost();
		if(!(hostToConnectTo==null)){
			thisCollabClient.setHost(hostToConnectTo);
			int portToConnectOn = setPort("join");
			if (!(portToConnectOn == -1)){
				thisCollabClient.setPort(portToConnectOn);
				thisCollabClient.handleMessageFromClientUI("#login");
				if(thisCollabClient.isConnected()){
					serverPanel.setVisible(false);
					hostMenu.setVisible(false);
					joinMenu.setVisible(true);
					openMenu.setVisible(false);
					notHosting.setVisible(true);
					collaborateMenu.setVisible(false);
					resetTabs();
					tabbedPane.setVisible(true);
				}
			}
		}
	}
	
	/**
	 * Queries the user for the port to join/host on.
	 * Returns an <code>int</code> between 1024 and 65535 (inclusive) if joining/hosting 
	 * and -1 (Cancel) otherwise.
	 * 
	 * @param type Whether to join or host.
	 * @return The port to join/host on.
	 */
	private int setPort(String type){
		int result = defaultPort;
		boolean wasSet = false;
		String messageText ="Which port number do you want to "+ type + " on?" + newLine +
				"The default is port 5700." + newLine +
				"The port should be between 1024 and 65535 (inclusive).";
		while (!wasSet){
			String portString = (String)JOptionPane.showInputDialog(
					mainWindow,
					messageText,
					"Set Port Number",
					JOptionPane.QUESTION_MESSAGE,
					null,
					null,
					result);
			try {
				result = Integer.parseInt(portString);
				if(result >= 1024 && result <= 65535){
					wasSet=true;
				}else{
					JOptionPane.showMessageDialog(
							mainWindow,
							"Invalid port number " + result + "!" + newLine + 
							"The port should be between 1024 and 65535 (inclusive).",
							"Invalid port number",
							JOptionPane.ERROR_MESSAGE);
				}
			}catch (Exception e){
				if(portString==null){//Cancel
					result = -1;
					wasSet=true;
				}else{
					JOptionPane.showMessageDialog(
							mainWindow,
							"Invalid port number " + portString + "!" + newLine + 
							"The port should be an integer between 1024 and 65535 (inclusive).",
							"Invalid port number",
							JOptionPane.ERROR_MESSAGE);
				}
			}
		}
		return result;
	}
	
	/**
	 * Checks if the array is all instances of <code>int num</code>.
	 * Returns true if it does not contain all instances of <code>num</code> and 
	 * false otherwise.
	 * 
	 * @param num The <code>int num</code> to check for in <code>ipAddress[4]</code>.
	 * @param array The <code>ipAddress[4]</code> to check.
	 * @return A <code>boolean</code> that is true if it does not contain all instances of <code>num</code> and 
	 * false otherwise.
	 */
	private boolean checkIPAddress(int num, Integer[] array){
		boolean isOkay = true;
		if(array[0]==array[1] && array[1]==array[2] && array[2]==array[3] && array[3]==num ){
			isOkay = false;
		}
		return isOkay;
	}
	
	/**
	 * Queries the user for the host to connect to.
	 * Returns a <code>String</code> representing a valid ip address, 
	 * a possible host name, or null (Cancel) otherwise.
	 * 
	 * @return A <code>String</code> representing a valid ip address, a possible host name, or null (Cancel) otherwise.
	 */
	private String setHost(){
		String result = defaultHost;
		boolean wasSet = false;
		String messageText ="Which host do you want to connect to?" + newLine +
				"The default host is \"" + result + "\"." + newLine +
				"The host can be an ipaddress or a host name.";
		while (!wasSet){
			String hostString = (String)JOptionPane.showInputDialog(
					mainWindow,
					messageText,
					"Set Host",
					JOptionPane.QUESTION_MESSAGE,
					null,
					null,
					result);
			if(hostString==null){//Cancel
				result = hostString;
				wasSet=true;
			}else if(hostString.equals("")){// Empty String not allowed
				JOptionPane.showMessageDialog(
						mainWindow,
						"Invalid host \"\"!" + newLine + 
						"The host can be an ipaddress or a host name.",
						"Invalid host",
						JOptionPane.ERROR_MESSAGE);
			}else {//If it is an ipaddress check if it is valid
				String ipAddressArray[] = hostString.split("\\.");
				if(ipAddressArray.length == 4){//This could be an ipaddress
					Integer ipaddress[] = new Integer[4];
					for(int addr = 0; addr < ipAddressArray.length; addr++){
						try{
							ipaddress[addr] = Integer.parseInt(ipAddressArray[addr]);
						}catch (Exception e){//If this is not all numbers that it could be a valid host name
							result = hostString;
							wasSet=true;
							for(int octet = 0; octet < addr; octet++){//Clean up
								ipaddress[octet] = null;
							}
							ipaddress = null;//Clean up
							return result;
						}
					}
					//Now we have an array if 4 integers let's check if it is a valid ipaddress
					boolean isValid = true;
					for(int octet = 0; octet < ipaddress.length; octet++){
						if(!(ipaddress[octet] >= 0 && ipaddress[octet] < 256)){
							isValid = false;
						}
					}
					if(isValid){
						if((checkIPAddress(0, ipaddress)) && (checkIPAddress(255, ipaddress))){//Check for 0.0.0.0 and 255.255.255.255
							//We have a proper ipaddress let's try it
							result = hostString;
							wasSet=true;
						}else{//We have 0.0.0.0 or 255.255.255.255
							JOptionPane.showMessageDialog(
									mainWindow,
									"Invalid ipaddress " + hostString + "!" + newLine + 
									"A valid ipaddress is between 0.0.0.0 and 255.255.255.255 exclusive.",
									"Invalid ipaddress",
									JOptionPane.ERROR_MESSAGE);
						}
					}else{//One or more of the octets is out of range
						JOptionPane.showMessageDialog(
								mainWindow,
								"Invalid ipaddress " + hostString + "!" + newLine + 
								"A valid ipaddress is between 0.0.0.0 and 255.255.255.255 exclusive.",
								"Invalid ipaddress",
								JOptionPane.ERROR_MESSAGE);
					}
					for(int octet = 0; octet < ipaddress.length; octet++){//Clean up
						ipaddress[octet] = null;
					}
					ipaddress = null;//Clean up
				}else{//This might be a valid host name so try it
					result = hostString;
					wasSet=true;
				}
			}
		}
		return result;
	}

	/**
	 * Resets the application tab at <code>index</code>.
	 * Returns a <code>boolean</code>, true if it was reset and false otherwise.
	 * 
	 * @param index The application tab to reset.
	 * @return A <code>boolean</code>, true if the application tab at <code>index</code> was reset and false otherwise.
	 */
	private boolean resetTab(int index){
		boolean wasReset = false;
		if(index >= CollabClient.minimumNumberOfCollaborationApps() && index < CollabClient.maximumNumberOfCollaborationApps()){
			thisCollabClient.removeCollaborationAppAt(index);
			tabbedApps[index] = thisCollabClient.getCollaborationApp(index);
			tabbedPane.setTitleAt(index,tabbedPane.getTitleAt(index).substring(tabbedPane.getTitleAt(index).length()-2));
			tabbedPane.setToolTipTextAt(index,tabbedPane.getTitleAt(index).substring(tabbedPane.getTitleAt(index).length()-2));
			tabbedPane.setComponentAt(index, tabbedApps[index]);
			tabbedPane.setEnabledAt(index, false);
			tabbedMenu[index].setText(tabbedMenu[index].getText().substring(0, 5));
			tabbedMenu[index].setVisible(false);
			wasReset=true;
		}
		return wasReset;
	}
	
	/**
	 * Resets all the application tabs to default.
	 */
	private void resetTabs(){
		for(int tab = 0; tab < tabbedApps.length; tab++){
			thisCollabClient.removeCollaborationAppAt(tab);
			tabbedApps[tab] = thisCollabClient.getCollaborationApp(tab);
			tabbedPane.setTitleAt(tab,tabbedPane.getTitleAt(tab).substring(tabbedPane.getTitleAt(tab).length()-2));
			tabbedPane.setToolTipTextAt(tab,tabbedPane.getTitleAt(tab).substring(tabbedPane.getTitleAt(tab).length()-2));
			tabbedPane.setComponentAt(tab, tabbedApps[tab]);
			tabbedPane.setEnabledAt(tab, false);
			tabbedMenu[tab].setText(tabbedMenu[tab].getText().substring(0, 5));
			tabbedMenu[tab].setVisible(false);
		}
	}
	
	/**
	 * Start a <code>new TicTacToe</code> at <code>tabNum</code>.
	 * 
	 * @param player0 The host.
	 * @param player1 The other player.
	 * @param tabNum The <code>tabNum</code> on which to start a <code>new TicTacToe</code>.
	 */
	private void startTicTacToe(String player0, String player1, int tabNum){
		if(resetTab(tabNum)){
			thisCollabClient.addCollaborationAppAt(new TicTacToe(thisCollabClient,player0,player1,tabNum),tabNum);
			tabbedApps[tabNum] = thisCollabClient.getCollaborationApp(tabNum);
			serverArea.setText(validApps[0][0] + " on Tab " + (tabNum + 1)
					+ newLine + serverArea.getText());
			tabbedPane.setTitleAt(tabNum, validApps[0][0] + tabbedPane.getTitleAt(tabNum));
			tabbedPane.setToolTipTextAt(tabNum, validApps[0][0] + tabbedPane.getToolTipTextAt(tabNum));
			tabbedPane.setComponentAt(tabNum, tabbedApps[tabNum]);
			tabbedPane.setEnabledAt(tabNum, true);
			tabbedMenu[tabNum].setText(tabbedMenu[tabNum].getText() + validApps[0][0] + " " + (tabNum + 1));
			if(getCollabClient().getCollabServer() == null){
				tabbedMenu[tabNum].setVisible(false);
			}else{
				tabbedMenu[tabNum].setVisible(true);
			}
		}
	}
	
	/**
	 * Ask which client to collaborate with.
	 * Returns the <code>loginID</code> of the client to collaborate with or <code>null</code> (Cancel). 
	 * 
	 * @return The <code>loginID</code> of the client to collaborate with or <code>null</code> (Cancel).
	 */
	private String askPlayer(){
		Thread clients[] = thisCollabClient.getCollabServer().getClientConnections();
		String possiblePlayers[] = new String[clients.length - 1];
		boolean subtractOne = false;
		//Get the Login ID from all the clients
		for(int client = 0; client < clients.length; client++){
			ConnectionToClient test = ((ConnectionToClient)clients[client]);
			int test2 = (Integer) (test.getInfo(CollabServer.IS_HOST));
//			if(!((boolean) ((ConnectionToClient) clients[client]).getInfo(CollabServer.IS_HOST)))
			if(test2 == 0)
			{
				if(!subtractOne){
					possiblePlayers[client] = (String) ((ConnectionToClient) clients[client]).getInfo(CollabServer.CLIENT_ID);
				}else {
					possiblePlayers[client - 1] = (String) ((ConnectionToClient) clients[client]).getInfo(CollabServer.CLIENT_ID);
				}
			}else{
				subtractOne = true;
			}
		}
		String s = (String)JOptionPane.showInputDialog(
		                    mainWindow,
		                    "Choose a player from the list:",
		                    "Select a player",
		                    JOptionPane.PLAIN_MESSAGE,
		                    null,
		                    possiblePlayers,
		                    possiblePlayers[0]);
		
		//CleanUp
		for(int player = 0; player < possiblePlayers.length; player++){
			possiblePlayers[player] = null;
		}
		possiblePlayers = null;
		return s;
	}

	/* (non-Javadoc)
	 * @see collaboration.common.CollabIF#display(java.lang.Object)
	 */
	@Override
	public void display(Object msg) {
		String message = msg.toString();
		if(msg.getClass().equals(StringBuffer.class)){//StringBuffer is an Application message
			if(message.startsWith("^server,")){
				serverArea.setText(message.substring(8) + newLine + serverArea.getText());
			}else {
				boolean found = false;
				for(int app = 0; app < validApps.length && !found; app++){
					if(message.startsWith("^" + validApps[app][1])){
						found = true;
						String[] messageBits = message.split(",");
						int index = (message.indexOf('#') + 1);
						try{
							int tab = Integer.parseInt(message.substring(index, (index + 1)));
							if (tab >= 0 && tab < numTabs){
								if(messageBits[1].equalsIgnoreCase("close")){
									thisCollabClient.removeCollaborationAppAt(tab);
									tabbedApps[tab] = thisCollabClient.getCollaborationApp(tab);
									tabbedPane.setTitleAt(tab,tabbedPane.getTitleAt(tab).substring(tabbedPane.getTitleAt(tab).length()-2));
									tabbedPane.setToolTipTextAt(tab,tabbedPane.getTitleAt(tab).substring(tabbedPane.getTitleAt(tab).length()-2));
									tabbedPane.setComponentAt(tab, tabbedApps[tab]);
									tabbedPane.setEnabledAt(tab, false);
									tabbedMenu[tab].setText(tabbedMenu[tab].getText().substring(0, 5));
									tabbedMenu[tab].setVisible(false);
									return;
								}else if(tabbedApps[tab].getUniqueID()!=Long.parseLong(validApps[app][1])){
									switch (app){
									case 0:
										startTicTacToe(messageBits[2], messageBits[3], tab);
										break;
									}
								}
								tabbedApps[tab].handleMessageFromAppUI((StringBuffer)msg);
							}
						} catch (Exception e){
							//Do nothing if the message is improperly formatted
						}
					}
				}
			}//Do nothing if the message is improperly formatted

		}else {//Not a StringBuffer
			if(message.contains("SERVER IS SHUTTING DOWN!")){
				resetWindow();
			}
			chatTextArea.setText(message + newLine + chatTextArea.getText());
		}
	}
	
	/**
	 * Asks the user if they really want to quit the application.
	 * If Yes quit, else continue.
	 */
	private void quit(){
		int option = -1000;// set to a number outside the range of JOptionPane
		String warn, title;
		warn = "Are you sure that you want to quit" + newLine +
				clientName + "?" ;
		title = "Quitting " + clientName;
		option = JOptionPane.showConfirmDialog(
				mainWindow,
				warn,
				title,
				JOptionPane.YES_NO_OPTION,
				JOptionPane.WARNING_MESSAGE,
				null);
		switch(option){
		case 0://Yes
			mainWindow.setVisible(false);
        	thisCollabClient.handleMessageFromClientUI("#quit");
			mainWindow.dispose();
			System.exit(0);
			break;
		}
	}
	
	/**
	 * Catch a command sent through the chat interface.
	 * Returns true if it might be a command and false otherwise.
	 *   
	 * @param toCheck The <code>String</code> to check.
	 * @return A code>boolean</code>, true if it might be a command and false otherwise.
	 */
	private boolean isCommand(String toCheck){
		boolean isCommand = false;
			if(toCheck.startsWith("#") || toCheck.startsWith("*")){
				isCommand = true;
			}
		return isCommand;
	}
	
	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		String message = e.getActionCommand();
		if(message.startsWith("#")){
			if(message.startsWith("#set")){
				//switch (message){
				//case "#setport":
				if(message.startsWith("#setport"))
					{
					int portToConnectOn = setPort("join");
					if (!(portToConnectOn == -1)){
						thisCollabClient.handleMessageFromClientUI(message + " " + portToConnectOn);
					}else{
						return;
					}
					//break;
					}
				//case "#sethost":
				if(message.startsWith("#sethost"))
				{
					String hostToConnectTo = setHost();
					if(!(hostToConnectTo==null)){
						thisCollabClient.handleMessageFromClientUI(message + " " + hostToConnectTo);
						if(thisCollabClient.getCollabServer()!=null && !(hostToConnectTo==defaultHost)){
							thisCollabClient.setCollabServer(null);
							serverPanel.setVisible(false);
							hostMenu.setVisible(false);
							openMenu.setVisible(false);
							notHosting.setVisible(true);
							collaborateMenu.setVisible(false);
						}
					}else {
						return;
					}
					//break;
				}
				
			}else if(message.startsWith("#quit")){
				quit();
				return;
			}else if(message.startsWith("#debug")){
				thisCollabClient.handleMessageFromClientUI("#debug");
				for(int app = 0; app < CollabClient.maximumNumberOfCollaborationApps(); app++){
					thisCollabClient.getCollaborationApp(app).setDebug(thisCollabClient.getDebug());
				}
				return;
			}else if(message.startsWith("#logoff")){
				hostMenu.setVisible(false);
				joinMenu.setVisible(false);
				openMenu.setVisible(false);
				collaborateMenu.setVisible(true);
				resetTabs();
			}
			thisCollabClient.handleMessageFromClientUI(message);
		}else if(message.startsWith("*") && thisCollabClient.getCollabServer() != null){
			if(message.startsWith("*setport")){
				int portToHostAndConnect = setPort("host");
				if (!(portToHostAndConnect == -1)){
					thisCollabClient.getCollabServer().handleMessageFromServerUI(message + " " +portToHostAndConnect);
				}
				return;
			}else if(message.startsWith("*quit")||message.startsWith("*close")){
				openMenu.setVisible(false);
				if(message.startsWith("*quit")){
					serverPanel.setVisible(false);
					hostMenu.setVisible(false);
					joinMenu.setVisible(false);
					resetTabs();
					collaborateMenu.setVisible(true);
				}
			}
			thisCollabClient.getCollabServer().handleMessageFromServerUI(message);
			return;
		}else if(message.equals("Host")){
			hosting();
			return;
		}else if(message.equals("Join")){
			joining();
			return;
		}else if(message.equals("Send")){
			String toSend = chatEnterArea.getText();
			if(!(toSend==null) && !(toSend.equals("")) && !isCommand(toSend)){
				try {
					thisCollabClient.sendToServer(toSend);
					chatEnterArea.setText(null);
				} catch (IOException e1) {
					//Nothing to do
				}
			}else{
				if(isCommand(toSend)){
					chatTextArea.setText("ERROR:Do not start chats with \"#\" or \"*\" !" + newLine + chatTextArea.getText());
				}
				chatEnterArea.setText(null);
			}
			return;
		}else if(message.equals(clientName)){
			quit();
			return;
		}else if(message.startsWith("Close")){
			int tabNum = (Integer.parseInt(message.substring(message.length()-1))-1);
			serverArea.setText("Close Tab " + (tabNum + 1) + newLine + serverArea.getText());
			StringBuffer msg = new StringBuffer("^" + tabbedApps[tabNum].getUniqueID()
					+ "#" + tabNum + ",close");
			try {
				thisCollabClient.sendToServer(msg);
			} catch (IOException e2) {
				//Error sending message do nothing
			}
			//Clean up
			msg = null;
			if(thisCollabClient.removeCollaborationAppAt(tabNum)){
				tabbedApps[tabNum] = thisCollabClient.getCollaborationApp(tabNum);
				tabbedPane.setTitleAt(tabNum,tabbedPane.getTitleAt(tabNum).substring(tabbedPane.getTitleAt(tabNum).length()-2));
				tabbedPane.setToolTipTextAt(tabNum,tabbedPane.getTitleAt(tabNum).substring(tabbedPane.getTitleAt(tabNum).length()-2));
				tabbedPane.setEnabledAt(tabNum, false);
				tabbedMenu[tabNum].setText(tabbedMenu[tabNum].getText().substring(0, 5));
				tabbedMenu[tabNum].setVisible(false);
			}
		}else{
			for(int validApp = 0; validApp < validApps.length; validApp++){
				if(e.getActionCommand()==validApps[validApp][0]){
					int tabNum = thisCollabClient.indexOfNextCollaborationApp();
					if (!(tabNum == -1)){
						switch (validApp){
						case 0:
							if(thisCollabClient.getCollabServer().getNumberOfClients() >= TicTacToe.NUM_PLAYERS){
								String player1 = askPlayer();
								if(!(player1==null)){
									String player0 = thisCollabClient.getLoginID();
									startTicTacToe(player0, player1, tabNum);
								}
								break;
							}else {
								serverArea.setText("ERROR: You need at least " + TicTacToe.NUM_PLAYERS +
										" collaborators to start this application."
										+ newLine + serverArea.getText());
							}
						}
					}else{
						serverArea.setText("ERROR: " + numTabs + " collaboration applications already running!"
								+ newLine + serverArea.getText());
					}
				}
			}
		}
	}

	/* Code from Umple*/
	
	//------------------------
	// INTERFACE
	//------------------------

	/* (non-Javadoc)
	 * @see collaboration.common.CollabIF#getCollabClient()
	 */
	public CollabClient getCollabClient()
	{
		return thisCollabClient;
	}

	/* (non-Javadoc)
	 * @see collaboration.common.CollabIF#setCollabClient(collaboration.client.CollabClient)
	 */
	public boolean setCollabClient(CollabClient newCollabClient)
	{
		boolean wasSet = false;
		if (newCollabClient == null)
		{
			//Unable to setCollabClient to null, as collabClientUI must always be associated to a collabClient
			return wasSet;
		}

		CollabIF existingCollabClientUI = newCollabClient.getCollabClientUI();
		if (existingCollabClientUI != null && !equals(existingCollabClientUI))
		{
			//Unable to setCollabClient, the current collabClient already has a collabClientUI, which would be orphaned if it were re-assigned
			return wasSet;
		}

		CollabClient oldCollabClient = thisCollabClient;
		thisCollabClient = newCollabClient;
		thisCollabClient.setCollabClientUI(this);

		if (oldCollabClient != null)
		{
			oldCollabClient.setCollabClientUI(null);
		}
		wasSet = true;
		return wasSet;
	}

	/* (non-Javadoc)
	 * @see collaboration.common.CollabIF#delete()
	 */
	public void delete()
	{
		CollabClient existingCollabClient = thisCollabClient;
		thisCollabClient = null;
		if (existingCollabClient != null)
		{
			existingCollabClient.setCollabClientUI(null);
		}
	}
}
